import asyncio
import os
import sys

from inspect import signature
from pydantic import BaseModel
from typing import Callable, Any
import signal

import psutil as psutil
from aiohttp import web
from aiohttp.web import Request
from settings import settings


class BaseHandler:
    class Response(BaseModel):
        result: Any = None

    __slots__ = ['app', 'request_queue_uuid']

    def __init__(self, request_queue_uuid: str) -> None:
        self.app = web.Application()
        self.request_queue_uuid: str = request_queue_uuid

        self.handle('ping', self.handle_ping)
        self.handle('get_pid', self.handle_get_pid)
        self.handle('get_service_name', self.handle_get_service_name)

    def receive_messages_in_task(self, loop: asyncio.AbstractEventLoop):
        def signal_handler(sig, frame):
            sys.exit(0)

        signal.signal(signal.SIGINT, signal_handler)
        signal.signal(signal.SIGTERM, signal_handler)
        loop.add_signal_handler(signal.SIGINT, signal_handler, 0, 0)
        loop.add_signal_handler(signal.SIGINT, signal_handler, 0, 0)

        async def task():
            try:
                await web._run_app(
                    self.app,
                    path=f'{settings.ROOT_PATH}/environment/sockets/{self.request_queue_uuid}.sock'
                )
            except Exception as e:
                print(e)
                sys.exit(0)

        loop.create_task(
            task()
        )

    async def receive_messages(self, loop: asyncio.AbstractEventLoop) -> None:
        try:
            self.receive_messages_in_task(loop)
            while True:
                await asyncio.sleep(3)
        except KeyboardInterrupt:
            sys.exit(0)

    def handle(self, name: str, handler: Callable):
        have_params = 'params' in signature(handler).parameters

        async def wrapper(request: Request):
            try:
                if have_params:
                    result = await handler(params=(await request.json())['params'])
                    return web.json_response(
                        text=BaseHandler.Response(
                            result=result
                        ).json()
                    )
                else:
                    return web.json_response(
                        text=BaseHandler.Response(
                            result=await handler()
                        ).json()
                    )

            except Exception as e:
                #print(f'in handle {handler.__name__}: {e}')
                return web.HTTPInternalServerError()

        self.app.router.add_post(f'/{name}', wrapper)

    @staticmethod
    def create_result_obj(result: bool) -> bool:
        return result

    @staticmethod
    async def handle_ping() -> str:
        return 'pong'

    @staticmethod
    async def handle_get_pid() -> int:
        return os.getpid()

    @staticmethod
    async def handle_get_service_name() -> str:
        return psutil.Process(os.getpid()).as_dict().get('cmdline')[1].split('/')[-2]
